;
(function($) {
	var rotator;

	$.fn.updateTransition = function(transition) {
		rotator.updateTransition(transition);
	}

	$.fn.updateTextEffect = function(effect) {
		rotator.updateTextEffect(effect);
	}

	$.fn.updateCpAlign = function(align) {
		rotator.updateCPanel(align);
	}

	$.fn.displayThumbs = function(display) {
		rotator.showThumbs(display);
	}

	$.fn.displayDButtons = function(display) {
		rotator.showDButtons(display);
	}

	$.fn.displayPlayButton = function(display) {
		rotator.showPlayButton(display);
	}

	$.fn.displayTooltip = function(display) {
		rotator.setTooltip(display);
	}

	$.fn.displayTimerBar = function(display) {
		rotator.setTimerBar(display);
	}

	$.fn.updateMouseoverPause = function(mouseover) {
		rotator.setMouseoverPause(mouseover);
	}

	$.fn.updateMouseoverCP = function(mouseover) {
		rotator.setMouseoverCP(mouseover);
	}

	$.fn.updateMouseoverDesc = function(mouseover) {
		rotator.setMouseoverDesc(mouseover);
	}

	$.fn.wtRotator = function(params) {
		var TOP_LEFT = "TL";
		var TOP_RIGHT = "TR";
		var TOP_CENTER = "TC";
		var BOTTOM_LEFT = "BL";
		var BOTTOM_RIGHT = "BR";
		var BOTTOM_CENTER = "BC";

		var EFFECTS = new Array(42);
		EFFECTS["fade"] = 0;
		EFFECTS["block.top"] = 1;
		EFFECTS["block.right"] = 2;
		EFFECTS["block.bottom"] = 3;
		EFFECTS["block.left"] = 4;
		EFFECTS["block.drop"] = 5;
		EFFECTS["diag.fade"] = 6;
		EFFECTS["diag.exp"] = 7;
		EFFECTS["rev.diag.fade"] = 8;
		EFFECTS["rev.diag.exp"] = 9;
		EFFECTS["block.fade"] = 10;
		EFFECTS["block.exp"] = 11;
		EFFECTS["block.top.zz"] = 12;
		EFFECTS["block.bottom.zz"] = 13;
		EFFECTS["block.left.zz"] = 14;
		EFFECTS["block.right.zz"] = 15;
		EFFECTS["spiral.in"] = 16;
		EFFECTS["spiral.out"] = 17;
		EFFECTS["vert.tl"] = 18;
		EFFECTS["vert.tr"] = 19;
		EFFECTS["vert.bl"] = 20;
		EFFECTS["vert.br"] = 21;
		EFFECTS["fade.left"] = 22;
		EFFECTS["fade.right"] = 23;
		EFFECTS["alt.left"] = 24;
		EFFECTS["alt.right"] = 25;
		EFFECTS["blinds.left"] = 26;
		EFFECTS["blinds.right"] = 27;
		EFFECTS["vert.random.fade"] = 28;
		EFFECTS["horz.tl"] = 29;
		EFFECTS["horz.tr"] = 30;
		EFFECTS["horz.bl"] = 31;
		EFFECTS["horz.br"] = 32;
		EFFECTS["fade.top"] = 33;
		EFFECTS["fade.bottom"] = 34;
		EFFECTS["alt.top"] = 35;
		EFFECTS["alt.bottom"] = 36;
		EFFECTS["blinds.top"] = 37;
		EFFECTS["blinds.bottom"] = 38;
		EFFECTS["horz.random.fade"] = 39;
		EFFECTS["none"] = 40;
		EFFECTS["random"] = 41;

		var TEXT_EFFECTS = new Array(4);
		TEXT_EFFECTS["fade"] = 0;
		TEXT_EFFECTS["down"] = 1;
		TEXT_EFFECTS["right"] = 2;
		TEXT_EFFECTS["none"] = 3;

		var DEFAULT_DELAY = 5000;
		var DURATION = 800;
		var ANIMATE_SPEED = 600;
		var TOOLTIP_DELAY = 600;
		var BAR_OPACITY = 0.5;
		var TEXT_EVENT = "update_text";

		// Vertical Stripes
		function VertStripes(rotator, areaWidth, areaHeight, stripeSize,
				bgColor, duration, delay) {
			var $stripes;
			var $arr;
			var total;
			var intervalId = null;

			// init stripes
			var init = function() {
				total = Math.ceil(areaWidth / stripeSize);
				var divs = "";
				for ( var i = 0; i < total; i++) {
					divs += "<div class='vpiece' id='" + i + "'></div>";
				}
				rotator.addToScreen(divs);

				$stripes = $("div.vpiece", rotator.$el);
				$arr = new Array(total);
				$stripes.each(function(n) {
					$(this).css({
						left : (n * stripeSize),
						height : areaHeight
					});
					$arr[n] = $(this);
				});
			}

			// clear animation
			this.clear = function() {
				clearInterval(intervalId);
				$stripes.stop(true).css({
					"z-index" : 2,
					opacity : 0
				});
			}

			// display content
			this.displayContent = function($img, effect) {
				setPieces($img, effect);
				if (effect == EFFECTS["vert.random.fade"]) {
					animateRandom($img);
				} else {
					animate($img, effect);
				}
			}

			// set image stripes
			var setPieces = function($img, effect) {
				switch (effect) {
				case EFFECTS["vert.tl"]:
				case EFFECTS["vert.tr"]:
					setVertPieces($img, -areaHeight, 0, stripeSize, false);
					break;
				case EFFECTS["vert.bl"]:
				case EFFECTS["vert.br"]:
					setVertPieces($img, areaHeight, 0, stripeSize, false);
					break;
				case EFFECTS["alt.left"]:
				case EFFECTS["alt.right"]:
					setVertPieces($img, 0, 0, stripeSize, true);
					break;
				case EFFECTS["blinds.left"]:
				case EFFECTS["blinds.right"]:
					setVertPieces($img, 0, 1, 0, false);
					break;
				default:
					setVertPieces($img, 0, 0, stripeSize, false);
				}
			}

			// set vertical stripes
			var setVertPieces = function($img, topPos, opacity, width, alt) {
				var imgSrc = $img.attr("src");
				var tOffset = (areaHeight - $img.height()) / 2;
				var lOffset = (areaWidth - $img.width()) / 2;
				for ( var i = 0; i < total; i++) {
					var xPos = ((-i * stripeSize) + lOffset);
					if (alt) {
						topPos = (i % 2) == 0 ? -areaHeight : areaHeight;
					}
					$($stripes.get(i)).css(
							{
								background : bgColor + " url('" + imgSrc
										+ "') no-repeat",
								backgroundPosition : xPos + "px " + tOffset
										+ "px",
								opacity : opacity,
								top : topPos,
								width : width,
								"z-index" : 3
							});
				}
			}

			// animate stripes
			var animate = function($img, effect) {
				var start, end, incr, limit;
				switch (effect) {
				case EFFECTS["vert.tl"]:
				case EFFECTS["vert.bl"]:
				case EFFECTS["fade.left"]:
				case EFFECTS["blinds.left"]:
				case EFFECTS["alt.left"]:
					start = 0;
					end = total - 1;
					incr = 1;
					break;
				default:
					start = total - 1;
					end = 0;
					incr = -1;
				}

				intervalId = setInterval(function() {
					$($stripes.get(start)).animate({
						top : 0,
						opacity : 1,
						width : stripeSize
					}, duration, "", function() {
						if ($(this).attr("id") == end) {
							rotator.setComplete($img);
						}
					});
					if (start == end) {
						clearInterval(intervalId);
					}
					start += incr;
				}, delay);
			}

			// animate random fade
			var animateRandom = function($img) {
				shuffleArray($arr);
				var i = 0;
				var count = 0;
				intervalId = setInterval(function() {
					$arr[i++].animate({
						opacity : 1
					}, duration, "", function() {
						if (++count == total) {
							rotator.setComplete($img);
						}
					});
					if (i == total) {
						clearInterval(intervalId);
					}
				}, delay);
			}

			init();
		}

		// Horizontal Stripes
		function HorzStripes(rotator, areaWidth, areaHeight, stripeSize,
				bgColor, duration, delay) {
			var $stripes;
			var $arr;
			var total;
			var intervalId = null;

			// init stripes
			var init = function() {
				total = Math.ceil(areaHeight / stripeSize);
				var divs = "";
				for ( var j = 0; j < total; j++) {
					divs += "<div class='hpiece' id='" + j + "'></div>";
				}
				rotator.addToScreen(divs);

				$stripes = $("div.hpiece", rotator.$el);
				$arr = new Array(total);
				$stripes.each(function(n) {
					$(this).css({
						top : (n * stripeSize),
						width : areaWidth
					});
					$arr[n] = $(this);
				});
			}

			// clear animation
			this.clear = function() {
				clearInterval(intervalId);
				$stripes.stop(true).css({
					"z-index" : 2,
					opacity : 0
				});
			}

			// display content
			this.displayContent = function($img, effect) {
				setPieces($img, effect);
				if (effect == EFFECTS["horz.random.fade"]) {
					animateRandom($img);
				} else {
					animate($img, effect);
				}
			}

			// set image stripes
			var setPieces = function($img, effect) {
				switch (effect) {
				case EFFECTS["horz.tr"]:
				case EFFECTS["horz.br"]:
					setHorzPieces($img, areaWidth, 0, stripeSize, false);
					break;
				case EFFECTS["horz.tl"]:
				case EFFECTS["horz.bl"]:
					setHorzPieces($img, -areaWidth, 0, stripeSize, false);
					break;
				case EFFECTS["alt.top"]:
				case EFFECTS["alt.bottom"]:
					setHorzPieces($img, 0, 0, stripeSize, true);
					break;
				case EFFECTS["blinds.top"]:
				case EFFECTS["blinds.bottom"]:
					setHorzPieces($img, 0, 1, 0, false);
					break;
				default:
					setHorzPieces($img, 0, 0, stripeSize, false);
				}
			}

			// set horizontal stripes
			var setHorzPieces = function($img, leftPos, opacity, height, alt) {
				var imgSrc = $img.attr("src");
				var tOffset = (areaHeight - $img.height()) / 2;
				var lOffset = (areaWidth - $img.width()) / 2;
				for ( var i = 0; i < total; i++) {
					var yPos = ((-i * stripeSize) + tOffset);
					if (alt) {
						leftPos = (i % 2) == 0 ? -areaWidth : areaWidth;
					}
					$($stripes.get(i)).css(
							{
								background : bgColor + " url('" + imgSrc
										+ "') no-repeat",
								backgroundPosition : lOffset + "px " + yPos
										+ "px",
								opacity : opacity,
								left : leftPos,
								height : height,
								"z-index" : 3
							});
				}
			}

			// animate stripes
			var animate = function($img, effect) {
				var start, end, incr;
				switch (effect) {
				case EFFECTS["horz.tl"]:
				case EFFECTS["horz.tr"]:
				case EFFECTS["fade.top"]:
				case EFFECTS["blinds.top"]:
				case EFFECTS["alt.top"]:
					start = 0;
					end = total - 1;
					incr = 1;
					break;
				default:
					start = total - 1;
					end = 0;
					incr = -1;
				}

				intervalId = setInterval(function() {
					$($stripes.get(start)).animate({
						left : 0,
						opacity : 1,
						height : stripeSize
					}, duration, "", function() {
						if ($(this).attr("id") == end) {
							rotator.setComplete($img);
						}
					});
					if (start == end) {
						clearInterval(intervalId);
					}
					start += incr;
				}, delay);
			}

			// animate random fade
			var animateRandom = function($img) {
				shuffleArray($arr);
				var i = 0;
				var count = 0;
				intervalId = setInterval(function() {
					$arr[i++].animate({
						opacity : 1
					}, duration, "", function() {
						if (++count == total) {
							rotator.setComplete($img);
						}
					});
					if (i == total) {
						clearInterval(intervalId);
					}
				}, delay);
			}

			init();
		}

		// class Blocks
		function Blocks(rotator, areaWidth, areaHeight, blockSize, bgColor,
				duration, delay) {
			var $blockArr;
			var $blocks;
			var $arr;
			var numRows;
			var numCols;
			var total;
			var intervalId;

			// init blocks
			var init = function() {
				numRows = Math.ceil(areaHeight / blockSize);
				numCols = Math.ceil(areaWidth / blockSize);
				total = numRows * numCols;
				var divs = "";
				for ( var i = 0; i < numRows; i++) {
					for ( var j = 0; j < numCols; j++) {
						divs += "<div class='block' id='" + i + "-" + j
								+ "'></div>";
					}
				}
				rotator.addToScreen(divs);
				$blocks = $("div.block", rotator.$el);
				$blocks.data({
					tlId : "0-0",
					trId : "0-" + (numCols - 1),
					blId : (numRows - 1) + "-0",
					brId : (numRows - 1) + "-" + (numCols - 1)
				});

				var k = 0;
				$arr = new Array(total);
				$blockArr = new Array(numRows);
				for ( var i = 0; i < numRows; i++) {
					$blockArr[i] = new Array(numCols);
					for ( var j = 0; j < numCols; j++) {
						$blockArr[i][j] = $arr[k++] = $blocks.filter(
								"#" + (i + "-" + j)).data("top", i * blockSize);
					}
				}
			}

			// clear blocks
			this.clear = function() {
				clearInterval(intervalId);
				$blocks.stop(true).css({
					"z-index" : 2,
					opacity : 0
				});
			}

			// display content
			this.displayContent = function($img, effect) {
				switch (effect) {
				case EFFECTS["diag.fade"]:
					setBlocks($img, 0, blockSize, 0);
					diagAnimate($img, {
						opacity : 1
					}, false);
					break;
				case EFFECTS["diag.exp"]:
					setBlocks($img, 0, 0, 0);
					diagAnimate($img, {
						opacity : 1,
						width : blockSize,
						height : blockSize
					}, false);
					break;
				case EFFECTS["rev.diag.fade"]:
					setBlocks($img, 0, blockSize, 0);
					diagAnimate($img, {
						opacity : 1
					}, true);
					break;
				case EFFECTS["rev.diag.exp"]:
					setBlocks($img, 0, 0, 0);
					diagAnimate($img, {
						opacity : 1,
						width : blockSize,
						height : blockSize
					}, true);
					break;
				case EFFECTS["block.fade"]:
					setBlocks($img, 0, blockSize, 0);
					randomAnimate($img);
					break;
				case EFFECTS["block.exp"]:
					setBlocks($img, 1, 0, 0);
					randomAnimate($img);
					break;
				case EFFECTS["block.drop"]:
					setBlocks($img, 1, blockSize, -(numRows * blockSize));
					randomAnimate($img);
					break;
				case EFFECTS["block.top.zz"]:
				case EFFECTS["block.bottom.zz"]:
					setBlocks($img, 0, blockSize, 0);
					horzZigZag($img, effect);
					break;
				case EFFECTS["block.left.zz"]:
				case EFFECTS["block.right.zz"]:
					setBlocks($img, 0, blockSize, 0);
					vertZigZag($img, effect);
					break;
				case EFFECTS["spiral.in"]:
					setBlocks($img, 0, blockSize, 0);
					spiral($img, false);
					break;
				case EFFECTS["spiral.out"]:
					setBlocks($img, 0, blockSize, 0);
					spiral($img, true);
					break;
				default:
					setBlocks($img, 1, 0, 0);
					dirAnimate($img, effect);
				}
			}

			// set blocks
			var setBlocks = function($img, opacity, size, tPos) {
				var tOffset = (areaHeight - $img.height()) / 2;
				var lOffset = (areaWidth - $img.width()) / 2;
				var imgSrc = $img.attr("src");
				for ( var i = 0; i < numRows; i++) {
					for ( var j = 0; j < numCols; j++) {
						var tVal = ((-i * blockSize) + tOffset);
						var lVal = ((-j * blockSize) + lOffset);
						$blockArr[i][j].css({
							background : bgColor + " url('" + imgSrc
									+ "') no-repeat",
							backgroundPosition : lVal + "px " + tVal + "px",
							opacity : opacity,
							top : (i * blockSize) + tPos,
							left : (j * blockSize),
							width : size,
							height : size,
							"z-index" : 3
						});
					}
				}
			}

			// diagonal effect
			var diagAnimate = function($img, props, rev) {
				var $array = new Array(total);
				var start, end, incr, lastId;
				var diagSpan = (numRows - 1) + (numCols - 1);
				if (rev) {
					start = diagSpan;
					end = -1;
					incr = -1;
					lastId = $blocks.data("tlId");
				} else {
					start = 0;
					end = diagSpan + 1;
					incr = 1;
					lastId = $blocks.data("brId");
				}

				var count = 0;
				while (start != end) {
					i = Math.min(numRows - 1, start);
					while (i >= 0) {
						j = Math.abs(i - start);
						if (j >= numCols) {
							break;
						}
						$array[count++] = $blockArr[i][j];
						i--;
					}
					start += incr;
				}

				count = 0;
				intervalId = setInterval(function() {
					$array[count++].animate(props, duration, "", function() {
						if ($(this).attr("id") == lastId) {
							rotator.setComplete($img);
						}
					});
					if (count == total) {
						clearInterval(intervalId);
					}
				}, delay);
			}

			// vertical zig zag effect
			var vertZigZag = function($img, effect) {
				var fwd = true;
				var i = 0, j, incr, lastId;
				if (effect == EFFECTS["block.left.zz"]) {
					lastId = (numCols % 2 == 0) ? $blocks.data("trId")
							: $blocks.data("brId");
					j = 0;
					incr = 1;
				} else {
					lastId = (numCols % 2 == 0) ? $blocks.data("tlId")
							: $blocks.data("blId");
					j = numCols - 1;
					incr = -1;
				}

				intervalId = setInterval(function() {
					$blockArr[i][j].animate({
						opacity : 1
					}, duration, "", function() {
						if ($(this).attr("id") == lastId) {
							rotator.setComplete($img);
						}
					});

					if ($blockArr[i][j].attr("id") == lastId) {
						clearInterval(intervalId);
					}

					(fwd ? i++ : i--);
					if (i == numRows || i < 0) {
						fwd = !fwd;
						i = (fwd ? 0 : numRows - 1);
						j += incr;
					}
				}, delay);
			}

			// horizontal zig zag effect
			var horzZigZag = function($img, effect) {
				var fwd = true;
				var i, j = 0, incr, lastId;
				if (effect == EFFECTS["block.top.zz"]) {
					lastId = (numRows % 2 == 0) ? $blocks.data("blId")
							: $blocks.data("brId");
					i = 0;
					incr = 1;
				} else {
					lastId = (numRows % 2 == 0) ? $blocks.data("tlId")
							: $blocks.data("trId");
					i = numRows - 1;
					incr = -1;
				}

				intervalId = setInterval(function() {
					$blockArr[i][j].animate({
						opacity : 1
					}, duration, "", function() {
						if ($(this).attr("id") == lastId) {
							rotator.setComplete($img);
						}
					});

					if ($blockArr[i][j].attr("id") == lastId) {
						clearInterval(intervalId);
					}

					(fwd ? j++ : j--);
					if (j == numCols || j < 0) {
						fwd = !fwd;
						j = (fwd ? 0 : numCols - 1);
						i += incr;
					}
				}, delay);
			}

			// vertical direction effect
			var dirAnimate = function($img, effect) {
				var $array = new Array(total);
				var lastId;
				var count = 0;
				switch (effect) {
				case EFFECTS["block.left"]:
					lastId = $blocks.data("brId");
					for ( var j = 0; j < numCols; j++) {
						for ( var i = 0; i < numRows; i++) {
							$array[count++] = $blockArr[i][j];
						}
					}
					break;
				case EFFECTS["block.right"]:
					lastId = $blocks.data("blId");
					for ( var j = numCols - 1; j >= 0; j--) {
						for ( var i = 0; i < numRows; i++) {
							$array[count++] = $blockArr[i][j];
						}
					}
					break;
				case EFFECTS["block.top"]:
					lastId = $blocks.data("brId");
					for ( var i = 0; i < numRows; i++) {
						for ( var j = 0; j < numCols; j++) {
							$array[count++] = $blockArr[i][j];
						}
					}
					break;
				default:
					lastId = $blocks.data("trId");
					for ( var i = numRows - 1; i >= 0; i--) {
						for ( var j = 0; j < numCols; j++) {
							$array[count++] = $blockArr[i][j];
						}
					}
				}
				count = 0;
				intervalId = setInterval(function() {
					$array[count++].animate({
						width : blockSize,
						height : blockSize
					}, duration, "", function() {
						if ($(this).attr("id") == lastId) {
							rotator.setComplete($img);
						}
					});
					if (count == total) {
						clearInterval(intervalId);
					}
				}, delay);
			}

			// random block effect
			var randomAnimate = function($img) {
				shuffleArray($arr);
				var i = 0;
				count = 0;
				intervalId = setInterval(function() {
					$arr[i].animate({
						top : $arr[i].data("top"),
						width : blockSize,
						height : blockSize,
						opacity : 1
					}, duration, "", function() {
						if (++count == total) {
							rotator.setComplete($img);
						}
					});
					i++;
					if (i == total) {
						clearInterval(intervalId);
					}
				}, delay);
			}

			// spiral effect
			var spiral = function($img, spiralOut) {
				var i = 0, j = 0;
				var rowCount = numRows - 1;
				var colCount = numCols - 1;
				var dir = 0;
				var limit = colCount;
				var $array = new Array();
				while (rowCount >= 0 && colCount >= 0) {
					var count = 0;
					while (true) {
						$array[$array.length] = $blockArr[i][j];
						if ((++count) > limit) {
							break;
						}
						switch (dir) {
						case 0:
							j++;
							break;
						case 1:
							i++;
							break;
						case 2:
							j--;
							break;
						case 3:
							i--;
						}
					}
					switch (dir) {
					case 0:
						dir = 1;
						limit = (--rowCount);
						i++;
						break;
					case 1:
						dir = 2;
						limit = (--colCount);
						j--;
						break;
					case 2:
						dir = 3;
						limit = (--rowCount);
						i--;
						break;
					case 3:
						dir = 0;
						limit = (--colCount);
						j++;
					}
				}
				if ($array.length > 0) {
					if (spiralOut) {
						$array.reverse();
					}
					var end = $array.length - 1;
					var lastId = $array[end].attr("id");
					var k = 0;
					intervalId = setInterval(function() {
						$array[k].animate({
							opacity : 1
						}, duration, "", function() {
							if ($(this).attr("id") == lastId) {
								rotator.setComplete($img);
							}
						});
						if (k == end) {
							clearInterval(intervalId);
						}
						k++;
					}, delay);
				}
			}

			init();
		}

		// class Rotator
		function Rotator($obj, opts) {
			// set options
			var screenWidth = getPosNumber(opts.width, 800);
			var screenHeight = getPosNumber(opts.height, 300);
			var margin = getNonNegNumber(opts.button_margin, 0);
			var globalEffect = opts.transition.toLowerCase();
			var duration = getPosNumber(opts.transition_speed, DURATION);
			var globalDelay = getPosNumber(opts.delay, DEFAULT_DELAY);
			var rotate = opts.auto_start;
			var cpAlign = opts.cpanel_align.toUpperCase();
			var buttonWidth = getPosNumber(opts.button_width, 24);
			var buttonHeight = getPosNumber(opts.button_height, 24);
			var displayThumbs = opts.display_thumbs;
			var displayDBtns = opts.display_dbuttons;
			var displayPlayBtn = opts.display_playbutton;
			var displayTooltip = opts.display_tooltip;
			var displayNumber = opts.display_numbers;
			var displayTimer = opts.display_timer;
			var cpMouseover = opts.cpanel_mouseover;
			var textMousover = opts.text_mouseover;
			var mouseoverPause = opts.mouseover_pause;
			var textEffect = opts.text_effect.toLowerCase();
			var shuffle = opts.shuffle;

			var numItems;
			var currIndex;
			var delay;
			var vStripes;
			var hStripes;
			var blocks;
			var timerId = null;
			var msie = (jQuery.browser.msie) ? true : false;
			var $mainScreen = $(".wt-rotator", $obj);
			var $mainLink = $mainScreen.find(">a:first");
			var $textBox = $mainScreen.find("div.desc");
			var $preloader = $mainScreen.find("div.preloader");
			var $cPanel = $mainScreen.find("div.c-panel");
			var $thumbPanel = $mainScreen.find("div.thumbnails");
			var $thumbList = $thumbPanel.find("ul>li");
			var $buttonPanel = $mainScreen.find("div.buttons");
			var $playBtn = $buttonPanel.find("div.play-btn");
			var $prevBtn = $buttonPanel.find("div.prev-btn");
			var $nextBtn = $buttonPanel.find("div.next-btn");
			var $timer;
			var $tooltip;
			var $items;
			var $innerText;
			this.$el = $obj;

			// init rotator
			this.init = function() {
				currIndex = 0;
				numItems = $thumbList.size();
				$items = new Array(numItems);
				var bgColor = $mainScreen.css("background-color");
				$mainScreen.css({
					width : screenWidth,
					height : screenHeight
				});

				// init components
				$preloader.css(
						{
							top : Math.round((screenHeight - $preloader
									.outerHeight()) / 2),
							left : Math.round((screenWidth - $preloader
									.outerWidth()) / 2)
						}).show();
				initTextBox();
				initItems();
				initButtons();
				initCPanel();
				initTimerBar();

				// init transition components
				vStripes = new VertStripes(this, screenWidth, screenHeight,
						getPosNumber(opts.vert_size, 50), bgColor, duration,
						getPosNumber(opts.vstripe_delay, 75));
				hStripes = new HorzStripes(this, screenWidth, screenHeight,
						getPosNumber(opts.horz_size, 50), bgColor, duration,
						getPosNumber(opts.hstripe_delay, 75));
				blocks = new Blocks(this, screenWidth, screenHeight,
						getPosNumber(opts.block_size, 100), bgColor, duration,
						getPosNumber(opts.block_delay, 50));

				// init image loading
				loadImg(0);

				// display initial image
				loadContent(currIndex);
			}

			// set complete
			this.setComplete = function($img) {
				showContent($img);
			}

			// add to screen
			this.addToScreen = function(content) {
				$mainLink.append(content);
			}

			// init text box
			var initTextBox = function() {
				$textBox.append("<div class='inner-text'></div>");
				$innerText = $textBox.find("div.inner-text");
				if (textMousover) {
					$mainScreen.hover(displayText, hideText);
				} else {
					$mainScreen.bind(TEXT_EVENT, updateText);
				}
			}

			// init control panel
			var initCPanel = function() {
				if (displayThumbs || displayDBtns || displayPlayBtn) {
					$cPanel.css({
						width : $buttonPanel.outerWidth(true)
								+ $thumbPanel.outerWidth(true),
						"margin-top" : margin,
						"margin-right" : 0,
						"margin-bottom" : margin,
						"margin-left" : margin
					});
					var cpWidth = $cPanel.outerWidth(true);
					var cpHeight = $cPanel.outerHeight(true);
					switch (cpAlign) {
					case TOP_LEFT:
						setCPanel(0, 0, -cpHeight, "left");
						break;
					case TOP_CENTER:
						setCPanel(0, Math.round((screenWidth - cpWidth) / 2),
								-cpHeight, "right");
						break;
					case TOP_RIGHT:
						setCPanel(0, (screenWidth - cpWidth), -cpHeight,
								"right");
						break;
					case BOTTOM_LEFT:
						setCPanel((screenHeight - cpHeight), 0, screenHeight,
								"left");
						break;
					case BOTTOM_CENTER:
						setCPanel((screenHeight - cpHeight), Math
								.round((screenWidth - cpWidth) / 2),
								screenHeight, "right");
						break;
					default:
						setCPanel((screenHeight - cpHeight),
								(screenWidth - cpWidth), screenHeight, "right");
					}

					if (cpMouseover) {
						$mainScreen.hover(displayCPanel, hideCPanel);
					}

					$cPanel.css("visibility", "visible");
				}
			}

			// set control panel attributes
			var setCPanel = function(yPos, xPos, offset, align) {
				$cPanel.data({
					offset : offset,
					pos : yPos
				}).css({
					top : (cpMouseover ? offset : yPos),
					left : xPos
				});
				$thumbPanel.css("float", align);
				$buttonPanel.css("float", align);
			}

			// init buttons
			var initButtons = function() {
				var props = {
					"margin-right" : margin,
					width : buttonWidth,
					height : buttonHeight
				};
				// config directional buttons
				if (displayDBtns) {
					$prevBtn.css(props).click(prevImg).mouseover(buttonOver)
							.mouseout(buttonOut).mousedown(preventDefault);
					$nextBtn.css(props).click(nextImg).mouseover(buttonOver)
							.mouseout(buttonOut).mousedown(preventDefault);
				} else {
					$prevBtn.hide();
					$nextBtn.hide();
				}

				// config play button
				if (displayPlayBtn) {
					if (rotate) {
						$playBtn.addClass("pause");
					}
					$playBtn.css(props).click(togglePlay).mouseover(buttonOver)
							.mouseout(buttonOut).mousedown(preventDefault);
				} else {
					$playBtn.hide();
				}

				if (mouseoverPause) {
					$mainScreen.hover(pause, play);
				}
			}

			// init timer bar
			var initTimerBar = function() {
				$mainScreen.append("<div id='timer'></div>");
				$timer = $mainScreen.find("#timer").data("pct", 1);
				if (displayTimer) {
					$timer.css({
						opacity : BAR_OPACITY,
						visibility : "visible"
					});
					switch (cpAlign) {
					case TOP_LEFT:
					case TOP_CENTER:
					case TOP_RIGHT:
						$timer.css("top", screenHeight - $timer.height());
						break;
					default:
						$timer.css("top", 0);
					}
				} else {
					$timer.hide();
				}
			}

			// init items
			var initItems = function() {
				var padding = $innerText.outerHeight() - $innerText.height();
				$thumbList
						.each(function(n) {
							var $imgLink = $(this).find(">a:first");
							$(this)
									.data(
											{
												imgurl : $imgLink.attr("href"),
												caption : $imgLink
														.attr("title"),
												effect : EFFECTS[$(this).attr(
														"effect")] != undefined ? EFFECTS[$(
														this).attr("effect")]
														: EFFECTS[globalEffect],
												delay : getPosNumber($(this)
														.attr("delay"),
														globalDelay)
											});
							initTextData($(this), padding);
							$items[n] = $(this);

							if (displayNumber) {
								$(this).append(n + 1);
							}
						});
				$innerText.css({
					width : "auto",
					height : "auto"
				}).html("");
				$textBox.css("visibility", "visible");

				if (shuffle) {
					shuffleItems();
				}

				if (displayThumbs) {
					$thumbList.css({
						width : buttonWidth,
						height : buttonHeight,
						"line-height" : buttonHeight + "px",
						"margin-right" : margin
					}).click(itemClick).mouseover(itemOver).mouseout(itemOut)
							.mousedown(preventDefault);
					initTooltip();
				} else {
					$thumbList.hide();
				}
			}

			// init text data
			var initTextData = function($item, padding) {
				var $p = $item.find(">p:first");
				var textWidth = getPosNumber(parseInt($p.css("width")), 280);
				var textHeight = getPosNumber(parseInt($p.css("height")), 0);
				$innerText.width(textWidth).html($p.html());
				if (textHeight < $innerText.height()) {
					textHeight = $innerText.height();
				}
				$item.data("textbox", {
					x : $p.css("left"),
					y : $p.css("top"),
					w : (textWidth + padding),
					h : (textHeight + padding)
				});
			}

			// init tool tip
			var initTooltip = function() {
				if (displayTooltip) {
					$tooltip = $("<div id='tool-tip'></div>");
					$mainScreen.after($tooltip);
					switch (cpAlign) {
					case TOP_LEFT:
					case TOP_CENTER:
					case TOP_RIGHT:
						$tooltip.data({
							bottom : true,
							yOffset : 23
						});
						break;
					default:
						$tooltip.data({
							bottom : false,
							yOffset : 5
						});
					}

					for ( var i = 0; i < $items.length; i++) {
						var caption = $items[i].data("caption");
						if (caption != "") {
							$items[i].mouseover(showTooltip).mouseout(
									hideTooltip).bind("mousemove", moveTooltip);
						}
					}
				}
			}

			// show tooltip
			var showTooltip = function(e) {
				var yOffset = $tooltip.data("bottom") ? $tooltip
						.data("yOffset") : -($tooltip.outerHeight() + $tooltip
						.data("yOffset"));
				$tooltip.html($items[$(this).index()].data("caption")).css({
					top : e.pageY + yOffset,
					left : e.pageX - 8
				}).stop(true, true).delay(TOOLTIP_DELAY).fadeIn(300);
			}

			// hide tooltip
			var hideTooltip = function() {
				$tooltip.stop(true, true).fadeOut(0);
			}

			// tooltip move
			var moveTooltip = function(e) {
				var yOffset = $tooltip.data("bottom") ? $tooltip
						.data("yOffset") : -($tooltip.outerHeight() + $tooltip
						.data("yOffset"));
				$tooltip.css({
					top : e.pageY + yOffset,
					left : e.pageX - 8
				});
			}

			// display control panel
			var displayCPanel = function() {
				$cPanel.stop(true).animate({
					top : $cPanel.data("pos"),
					opacity : 1
				}, ANIMATE_SPEED);
			}

			// hide control panel
			var hideCPanel = function() {
				$cPanel.stop(true).animate({
					top : $cPanel.data("offset"),
					opacity : 0
				}, ANIMATE_SPEED);
			}

			// on item click
			var itemClick = function() {
				resetTimer();
				currIndex = $(this).index();
				loadContent(currIndex);
				return false;
			}

			// on item mouseover
			var itemOver = function() {
				$(this).addClass("thumb-over");
			}

			// on item mouseout
			var itemOut = function() {
				$(this).removeClass("thumb-over");
			}

			// go to previous image
			var prevImg = function() {
				resetTimer();
				currIndex = (currIndex > 0) ? (currIndex - 1) : (numItems - 1);
				loadContent(currIndex);
				return false;
			}

			// go to next image
			var nextImg = function() {
				resetTimer();
				currIndex = (currIndex < numItems - 1) ? (currIndex + 1) : 0;
				loadContent(currIndex);
				return false;
			}

			// play/pause
			var togglePlay = function() {
				rotate = !rotate;
				$(this).toggleClass("pause", rotate);
				rotate ? startTimer() : pauseTimer();
				return false;
			}

			// play
			var play = function() {
				rotate = true;
				$playBtn.toggleClass("pause", rotate);
				startTimer();
			}

			// pause
			var pause = function() {
				rotate = false;
				$playBtn.toggleClass("pause", rotate);
				pauseTimer();
			}

			// on button over
			var buttonOver = function() {
				$(this).addClass("button-over");
			}

			// on button out
			var buttonOut = function() {
				$(this).removeClass("button-over");
			}

			// update text box
			var updateText = function(e) {
				if (!$textBox.data("visible")) {
					$textBox.data("visible", true);
					var text = $items[currIndex].find(">p:first").html();
					if (text && text.length > 0) {
						var data = $items[currIndex].data("textbox");
						switch (TEXT_EFFECTS[textEffect]) {
						case TEXT_EFFECTS["fade"]:
							fadeInText(text, data);
							break;
						case TEXT_EFFECTS["down"]:
							expandText(text, {
								opacity : 1,
								top : data.y,
								left : data.x,
								width : data.w,
								height : 0
							}, {
								height : data.h
							});
							break;
						case TEXT_EFFECTS["right"]:
							expandText(text, {
								opacity : 1,
								top : data.y,
								left : data.x,
								width : 0,
								height : data.h
							}, {
								width : data.w
							});
							break;
						default:
							showText(text, data);
						}
					}
				}
			}

			// reset text box
			var resetText = function() {
				$textBox.data("visible", false).stop(true).css({
					opacity : 0
				});
			}

			// expand text effect
			var expandText = function(text, props1, props2) {
				$innerText.html("");
				$textBox.stop(true).css(props1).animate(props2, ANIMATE_SPEED,
						function() {
							$innerText.html(text);
							if (msie) {
								this.style.removeAttribute('filter');
							}
						});
			}

			// fade in text effect
			var fadeInText = function(text, data) {
				$innerText.html(text);
				$textBox.stop(true).css({
					top : data.y,
					left : data.x,
					width : data.w,
					height : data.h
				}).animate({
					opacity : 1
				}, ANIMATE_SPEED, function() {
					if (msie) {
						this.style.removeAttribute('filter');
					}
				});
			}

			// show text effect
			var showText = function(text, data) {
				$textBox.stop(true).css({
					opacity : 1,
					top : data.y,
					left : data.x,
					width : data.w,
					height : data.h
				});
				$innerText.html(text);
			}

			// display text panel on mouseover
			var displayText = function() {
				$mainScreen.unbind(TEXT_EVENT).bind(TEXT_EVENT, updateText)
						.trigger(TEXT_EVENT);
			}

			// hide text panel on mouseovers
			var hideText = function() {
				$mainScreen.unbind(TEXT_EVENT);
				resetText();
			}

			// load current content
			var loadContent = function(i) {
				// select thumb
				$thumbList.filter(".curr-thumb").removeClass("curr-thumb");
				$($thumbList.get(i)).addClass("curr-thumb");

				// set delay
				delay = $items[i].data("delay");

				// reset text
				resetText();

				// set link
				var $currLink = $items[i].find(">a:nth-child(2)");
				var href = $currLink.attr("href");
				if (href) {
					$mainLink.unbind("click").css({
						cursor : "pointer"
					}).attr({
						href : href,
						target : $currLink.attr("target")
					});
				} else {
					$mainLink.click(preventDefault).css({
						cursor : "default"
					});
				}

				// load image
				if ($items[i].data("img")) {
					$preloader.hide();
					displayContent($items[i].data("img"));
				} else {
					// load new image
					var $img = $("<img class='main-img'/>");
					$img.attr("src", $items[i].data("imgurl"));
					if (!$img[0].complete) {
						$preloader.show();
						$img.load(function() {
							$preloader.hide();
							storeImg($items[i], $(this));
							displayContent($(this));
						}).error(function() {
							alert("Error loading image");
						});
					} else {
						$preloader.hide();
						storeImg($items[i], $img);
						displayContent($img);
					}
				}
			}

			// display content
			var displayContent = function($img) {
				// clear
				vStripes.clear();
				hStripes.clear();
				blocks.clear();

				// get effect number
				var effect = $items[currIndex].data("effect");
				if (effect == EFFECTS["none"]) {
					showContent($img);
					return;
				}

				if (effect == EFFECTS["random"]) {
					effect = Math.floor(Math.random() * (EFFECTS.length - 2));
				}

				if (effect == EFFECTS["fade"]) {
					fadeInContent($img);
				} else if (effect < EFFECTS["vert.tl"]) {
					blocks.displayContent($img, effect);
				} else if (effect < EFFECTS["horz.tl"]) {
					vStripes.displayContent($img, effect);
				} else {
					hStripes.displayContent($img, effect);
				}
			}

			// display content (no effect)
			var showContent = function($img) {
				$mainScreen.trigger(TEXT_EVENT);
				$("img.main-img", $mainLink).removeAttr("id").hide();
				$img.attr("id", "curr-img").show();
				startTimer();
			}

			// display content (fade effect)
			var fadeInContent = function($img) {
				$("img#curr-img", $mainLink).stop(true, true);
				$("img.main-img", $mainLink).removeAttr("id").css("z-index", 0);
				$img.attr("id", "curr-img").css("z-index", 1).stop(true, true)
						.fadeIn(
								duration,
								function() {
									$("img.main-img:not('#curr-img')",
											$mainLink).hide();
									$mainScreen.trigger(TEXT_EVENT);
									startTimer();
								});
			}

			// load image
			var loadImg = function(loadIndex) {
				var $item = $items[loadIndex];
				var $img = $("<img class='main-img'/>");
				$img.attr("src", $item.data("imgurl"));
				$img.load(function() {
					if (!$item.data("img")) {
						storeImg($item, $(this));
					}
					loadIndex++
					if (loadIndex < $items.length) {
						loadImg(loadIndex);
					}
				}).error(function() {
					// error loading image, continue next
					loadIndex++
					if (loadIndex < $items.length) {
						loadImg(loadIndex);
					}
				});
			}

			// process & store image
			var storeImg = function($item, $img) {
				$mainLink.append($img);
				var tDiff = (screenHeight - $img.height()) / 2;
				var lDiff = (screenWidth - $img.width()) / 2
				var top = 0, left = 0, vPad = 0, hPad = 0;
				if (tDiff > 0) {
					vPad = tDiff;
				} else if (tDiff < 0) {
					top = tDiff;
				}
				if (lDiff > 0) {
					hPad = lDiff;
				} else if (lDiff < 0) {
					left = lDiff;
				}
				$img.css({
					top : top,
					left : left,
					"padding-top" : vPad,
					"padding-bottom" : vPad,
					"padding-left" : hPad,
					"padding-right" : hPad
				});
				$item.data("img", $img);
			}

			// start timer
			var startTimer = function() {
				if (rotate && timerId == null) {
					var duration = Math.round($timer.data("pct") * delay);
					$timer.animate({
						width : (screenWidth + 2)
					}, duration);
					timerId = setTimeout(nextImg, duration);
				}
			}

			// reset timer
			var resetTimer = function() {
				clearTimeout(timerId);
				timerId = null;
				$timer.stop(true).width(0).data("pct", 1);
			}

			// pause timer
			var pauseTimer = function() {
				clearTimeout(timerId);
				timerId = null;
				var pct = 1 - ($timer.width() / (screenWidth + 2));
				$timer.stop(true).data("pct", pct);
			}

			// shuffle items
			var shuffleItems = function() {
				for ( var i = 0; i < $items.length; i++) {
					var ri = Math.floor(Math.random() * $items.length);
					var temp = $items[i];
					$items[i] = $items[ri];
					$items[ri] = temp;
				}
			}

			// prevent default behavior
			var preventDefault = function() {
				return false;
			}

			/* preview */
			this.setMouseoverPause = function(val) {
				mouseoverPause = val;
				if (mouseoverPause) {
					$mainScreen.bind("mouseenter", pause).bind("mouseleave",
							play);
				} else {
					$mainScreen.unbind("mouseenter", pause).unbind(
							"mouseleave", play);
				}
			}

			this.setMouseoverCP = function(val) {
				cpMouseover = val;
				if (cpMouseover) {
					$mainScreen.bind("mouseenter", displayCPanel).bind(
							"mouseleave", hideCPanel);
					hideCPanel();
				} else {
					$mainScreen.unbind("mouseenter", displayCPanel).unbind(
							"mouseleave", hideCPanel);
					displayCPanel();
				}
			}

			this.setMouseoverDesc = function(val) {
				descMouseover = val;
				if (descMouseover) {
					$mainScreen.bind("mouseenter", displayText).bind(
							"mouseleave", hideText);
					hideText();
				} else {
					$mainScreen.unbind("mouseenter", displayText).unbind(
							"mouseleave", hideText);
					displayText();
				}
			}

			this.updateTransition = function(val) {
				globalEffect = val;
				$thumbList.each(function(n) {
					$(this).data("effect", EFFECTS[globalEffect]);
				});
			}

			this.updateTextEffect = function(val) {
				textEffect = val;
			}

			this.showThumbs = function(display) {
				displayThumbs = display;
				if (displayThumbs) {
					$thumbPanel.width(240).show();
				} else {
					$thumbPanel.hide().width(0);
				}
				this.updateCPanel(cpAlign);
			}

			this.showDButtons = function(display) {
				displayDBtns = display;
				if (displayDBtns) {
					$prevBtn.show();
					$nextBtn.show();
					if (displayPlayBtn) {
						$buttonPanel.width($playBtn.outerWidth(true)
								+ $prevBtn.outerWidth(true)
								+ $nextBtn.outerWidth(true));
					} else {
						$buttonPanel.width($prevBtn.outerWidth(true)
								+ $nextBtn.outerWidth(true));
					}
				} else {
					$prevBtn.hide();
					$nextBtn.hide();
					if (displayPlayBtn) {
						$buttonPanel.width($playBtn.outerWidth(true));
					} else {
						$buttonPanel.width(0);
					}
				}
				this.updateCPanel(cpAlign);
			}

			this.showPlayButton = function(display) {
				displayPlayBtn = display;
				if (displayPlayBtn) {
					$playBtn.show();
					if (displayDBtns) {
						$buttonPanel.width($playBtn.outerWidth(true)
								+ $prevBtn.outerWidth(true)
								+ $nextBtn.outerWidth(true));
					} else {
						$buttonPanel.width($playBtn.outerWidth(true));
					}
				} else {
					$playBtn.hide();
					if (displayDBtns) {
						$buttonPanel.width($prevBtn.outerWidth(true)
								+ $nextBtn.outerWidth(true));
					} else {
						$buttonPanel.width(0);
					}
				}
				this.updateCPanel(cpAlign);
			}

			this.setTooltip = function(display) {
				displayTooltip = display;
				if (displayTooltip) {
					$thumbList.bind("mouseover", showTooltip).bind("mousemove",
							moveTooltip).mouseout(hideTooltip);
				} else {
					$thumbList.unbind("mouseover", showTooltip).unbind(
							"mousemove", moveTooltip).unbind("mouseout",
							hideTooltip);
				}
			}

			this.setTimerBar = function(display) {
				displayTimer = display;
				if (displayTimer) {
					$timer.css({
						visibility : "visible"
					}).show();
				} else {
					$timer.css({
						visibility : "hidden"
					}).hide();
				}
			}

			this.updateCPanel = function(align) {
				cpAlign = align;
				$cPanel.css({
					width : $buttonPanel.outerWidth(true)
							+ $thumbPanel.outerWidth(true)
				});
				switch (cpAlign) {
				case TOP_LEFT:
					setCPanel(0, 0, -$cPanel.outerHeight(true), "left");
					$timer.css("top", screenHeight - $timer.height());
					$tooltip.data({
						bottom : true,
						yOffset : 23
					});
					break;
				case TOP_CENTER:
					setCPanel(0, Math.floor((screenWidth - $cPanel
							.outerWidth(true)) / 2),
							-$cPanel.outerHeight(true), "right");
					$timer.css("top", screenHeight - $timer.height());
					$tooltip.data({
						bottom : true,
						yOffset : 23
					});
					break;
				case TOP_RIGHT:
					setCPanel(0, screenWidth - $cPanel.outerWidth(true),
							-$cPanel.outerHeight(true), "right");
					$timer.css("top", screenHeight - $timer.height());
					$tooltip.data({
						bottom : true,
						yOffset : 23
					});
					break;
				case BOTTOM_LEFT:
					setCPanel(screenHeight - $cPanel.outerHeight(true), 0,
							screenHeight, "left");
					$timer.css("top", 0);
					$tooltip.data({
						bottom : false,
						yOffset : 5
					});
					break;
				case BOTTOM_CENTER:
					setCPanel(screenHeight - $cPanel.outerHeight(true),
							Math
									.floor((screenWidth - $cPanel
											.outerWidth(true)) / 2),
							screenHeight, "right");
					$timer.css("top", 0);
					$tooltip.data({
						bottom : false,
						yOffset : 5
					});
					break;
				default:
					setCPanel(screenHeight - $cPanel.outerHeight(true),
							screenWidth - $cPanel.outerWidth(true),
							screenHeight, "right");
					$timer.css("top", 0);
					$tooltip.data({
						bottom : false,
						yOffset : 5
					});
				}
			}
		}

		// get positive number
		var getPosNumber = function(val, defaultVal) {
			if (!isNaN(val) && val > 0) {
				return val;
			}
			return defaultVal;
		}

		// get nonnegative number
		var getNonNegNumber = function(val, defaultVal) {
			if (!isNaN(val) && val >= 0) {
				return val;
			}
			return defaultVal;
		}

		// shuffle array
		var shuffleArray = function(arr) {
			var total = arr.length;
			for ( var i = 0; i < total; i++) {
				var ri = Math.floor(Math.random() * total);
				var temp = arr[i];
				arr[i] = arr[ri];
				arr[ri] = temp;
			}
		}

		var defaults = {
			width : 800,
			height : 300,
			button_width : 24,
			button_height : 24,
			button_margin : 4,
			auto_start : true,
			delay : DEFAULT_DELAY,
			transition : "fade",
			transition_speed : DURATION,
			cpanel_align : BOTTOM_RIGHT,
			display_thumbs : true,
			display_dbuttons : true,
			display_playbutton : true,
			display_tooltip : true,
			display_numbers : true,
			display_timer : true,
			mouseover_pause : false,
			cpanel_mouseover : false,
			text_mouseover : false,
			text_effect : "fade",
			shuffle : false,
			block_size : 100,
			vert_size : 50,
			horz_size : 50,
			block_delay : 50,
			vstripe_delay : 75,
			hstripe_delay : 75
		};

		var opts = $.extend({}, defaults, params);
		return this.each(function() {
			rotator = new Rotator($(this), opts);
			rotator.init();
		});
	}
})(jQuery);